home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / dawz.c < prev    next >
C/C++ Source or Header  |  1996-03-14  |  45KB  |  1,505 lines

  1. /*
  2.  * Program:    Dawz mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    24 June 1992
  13.  * Last Edited:    14 March 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. #include <fcntl.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <time.h>
  44. #include <sys/stat.h>
  45. #include <dos.h>
  46. #include <io.h>
  47. #include "dawz.h"
  48. #include "rfc822.h"
  49. #include "dummy.h"
  50. #include "misc.h"
  51.  
  52. /* Dawz mail routines */
  53.  
  54.  
  55. /* Driver dispatch used by MAIL */
  56.  
  57. DRIVER dawzdriver = {
  58.   "dawz",            /* driver name */
  59.   (DRIVER *) NIL,        /* next driver */
  60.   dawz_valid,            /* mailbox is valid for us */
  61.   dawz_parameters,        /* manipulate parameters */
  62.   dawz_find,            /* find mailboxes */
  63.   dawz_find_bboards,        /* find bboards */
  64.   dawz_find_all,        /* find all mailboxes */
  65.   dawz_find_bboards,        /* find all bboards */
  66.   dawz_subscribe,        /* subscribe to mailbox */
  67.   dawz_unsubscribe,        /* unsubscribe from mailbox */
  68.   dawz_subscribe_bboard,    /* subscribe to bboard */
  69.   dawz_subscribe_bboard,    /* unsubscribe (same as subscribe) */
  70.   dawz_create,            /* create mailbox */
  71.   dawz_delete,            /* delete mailbox */
  72.   dawz_rename,            /* rename mailbox */
  73.   dawz_open,            /* open mailbox */
  74.   dawz_close,            /* close mailbox */
  75.   dawz_fetchfast,        /* fetch message "fast" attributes */
  76.   dawz_fetchflags,        /* fetch message flags */
  77.   dawz_fetchstructure,        /* fetch message envelopes */
  78.   dawz_fetchheader,        /* fetch message header only */
  79.   dawz_fetchtext,        /* fetch message body only */
  80.   dawz_fetchbody,        /* fetch message body section */
  81.   dawz_setflag,            /* set message flag */
  82.   dawz_clearflag,        /* clear message flag */
  83.   dawz_search,            /* search for message based on criteria */
  84.   dawz_ping,            /* ping mailbox to see if still alive */
  85.   dawz_check,            /* check for new messages */
  86.   dawz_expunge,            /* expunge deleted messages */
  87.   dawz_copy,            /* copy messages to another mailbox */
  88.   dawz_move,            /* move messages to another mailbox */
  89.   dawz_append,            /* append string message to mailbox */
  90.   dawz_gc            /* garbage collect stream */
  91. };
  92.  
  93.                 /* prototype stream */
  94. MAILSTREAM dawzproto = {&dawzdriver};
  95.  
  96. /* Dawz mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100.  
  101. DRIVER *dawz_valid (char *name)
  102. {
  103.   return dawz_isvalid (name) ? &dawzdriver : (DRIVER *) NIL;
  104. }
  105.  
  106.  
  107. /* Dawz mail test for valid mailbox
  108.  * Accepts: mailbox name
  109.  * Returns: T if valid, NIL otherwise
  110.  */
  111.  
  112. long dawz_isvalid (char *name)
  113. {
  114.   int fd;
  115.   long ret = NIL;
  116.   char *s,tmp[MAILTMPLEN];
  117.   struct stat sbuf;
  118.   errno = EINVAL;        /* assume invalid argument */
  119.                 /* if file, get its status */
  120.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  121.       mailboxfile (tmp,name) && !stat (tmp,&sbuf)) {
  122.     if (!sbuf.st_size)errno = 0;/* empty file */
  123.     else if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) >= 0) {
  124.       memset (tmp,'\0',MAILTMPLEN);
  125.       if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
  126.       (s[1] == '\012')) {    /* valid format? */
  127.     *s = '\0';        /* tie off header */
  128.                 /* must begin with dd-mmm-yy" */
  129.     ret = (((tmp[2] == '-' && tmp[6] == '-') ||
  130.         (tmp[1] == '-' && tmp[5] == '-')) &&
  131.            (s = strchr (tmp+20,',')) && strchr (s+2,';')) ? T : NIL;
  132.       }
  133.       else errno = -1;        /* bogus format */
  134.       close (fd);        /* close the file */
  135.     }
  136.   }
  137.   return ret;            /* return what we should */
  138. }
  139.  
  140.  
  141. /* Dawz manipulate driver parameters
  142.  * Accepts: function code
  143.  *        function-dependent value
  144.  * Returns: function-dependent return value
  145.  */
  146.  
  147. void *dawz_parameters (long function,void *value)
  148. {
  149.   return NIL;
  150. }
  151.  
  152. /* Dawz mail find list of mailboxes
  153.  * Accepts: mail stream
  154.  *        pattern to search
  155.  */
  156.  
  157. void dawz_find (MAILSTREAM *stream,char *pat)
  158. {
  159.   if (stream) dummy_find (stream,pat);
  160. }
  161.  
  162.  
  163. /* Dawz mail find list of bboards
  164.  * Accepts: mail stream
  165.  *        pattern to search
  166.  */
  167.  
  168. void dawz_find_bboards (MAILSTREAM *stream,char *pat)
  169. {
  170.   /* Always a no-op */
  171. }
  172.  
  173.  
  174. /* Dawz mail find list of all mailboxes
  175.  * Accepts: mail stream
  176.  *        pattern to search
  177.  */
  178.  
  179. void dawz_find_all (MAILSTREAM *stream,char *pat)
  180. {
  181.   if (stream) dummy_find_all (stream,pat);
  182. }
  183.  
  184. /* Dawz mail subscribe to mailbox
  185.  * Accepts: mail stream
  186.  *        mailbox to add to subscription list
  187.  * Returns: T on success, NIL on failure
  188.  */
  189.  
  190. long dawz_subscribe (MAILSTREAM *stream,char *mailbox)
  191. {
  192.   return dummy_subscribe (stream,mailbox);
  193. }
  194.  
  195.  
  196. /* Dawz mail unsubscribe to mailbox
  197.  * Accepts: mail stream
  198.  *        mailbox to delete from subscription list
  199.  * Returns: T on success, NIL on failure
  200.  */
  201.  
  202. long dawz_unsubscribe (MAILSTREAM *stream,char *mailbox)
  203. {
  204.   return dummy_unsubscribe (stream,mailbox);
  205. }
  206.  
  207.  
  208. /* Dawz mail subscribe to bboard
  209.  * Accepts: mail stream
  210.  *        bboard to add to subscription list
  211.  * Returns: T on success, NIL on failure
  212.  */
  213.  
  214. long dawz_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  215. {
  216.   return NIL;            /* never valid for Dawz */
  217. }
  218.  
  219. /* Dawz mail create mailbox
  220.  * Accepts: MAIL stream
  221.  *        mailbox name to create
  222.  * Returns: T on success, NIL on failure
  223.  */
  224.  
  225. long dawz_create (MAILSTREAM *stream,char *mailbox)
  226. {
  227.   return dummy_create (stream,mailbox);
  228. }
  229.  
  230.  
  231. /* Dawz mail delete mailbox
  232.  * Accepts: MAIL stream
  233.  *        mailbox name to delete
  234.  * Returns: T on success, NIL on failure
  235.  */
  236.  
  237. long dawz_delete (MAILSTREAM *stream,char *mailbox)
  238. {
  239.   return dummy_delete (stream,mailbox);
  240. }
  241.  
  242.  
  243. /* Dawz mail rename mailbox
  244.  * Accepts: MAIL stream
  245.  *        old mailbox name
  246.  *        new mailbox name (or NIL for delete)
  247.  * Returns: T on success, NIL on failure
  248.  */
  249.  
  250. long dawz_rename (MAILSTREAM *stream,char *old,char *new)
  251. {
  252.   return dummy_rename (stream,old,new);
  253. }
  254.  
  255. /* Dawz mail open
  256.  * Accepts: stream to open
  257.  * Returns: stream on success, NIL on failure
  258.  */
  259.  
  260. MAILSTREAM *dawz_open (MAILSTREAM *stream)
  261. {
  262.   long i;
  263.   int fd;
  264.   char *s;
  265.   char tmp[MAILTMPLEN];
  266.   struct stat sbuf;
  267.                 /* return prototype for OP_PROTOTYPE call */
  268.   if (!stream) return &dawzproto;
  269.   if (LOCAL) {            /* close old file if stream being recycled */
  270.     dawz_close (stream);    /* dump and save the changes */
  271.     stream->dtb = &dawzdriver;    /* reattach this driver */
  272.     mail_free_cache (stream);    /* clean up cache */
  273.   }
  274.   else {            /* flush flagstring and flags if any */
  275.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  276.     for (i = 0; i < NUSERFLAGS; ++i) stream->user_flags[i] = NIL;
  277.   }
  278.   if (!mailboxfile (tmp,stream->mailbox))
  279.     return (MAILSTREAM *) dawz_badname (tmp,stream->mailbox);
  280.   if (((fd = open (tmp,O_BINARY|(stream->rdonly ? O_RDONLY:O_RDWR),NIL)) < 0)
  281.       && (strcmp (ucase (stream->mailbox),"INBOX") ||
  282.       ((fd = open (tmp,O_BINARY|O_RDWR|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))
  283.        < 0))) {        /* open, possibly creating INBOX */
  284.     sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  285.     mm_log (tmp,ERROR);
  286.     return NIL;
  287.   }
  288.   stream->local = fs_get (sizeof (DAWZLOCAL));
  289.                 /* canonicalize the stream mailbox name */
  290.   fs_give ((void **) &stream->mailbox);
  291.   if (s = strchr ((s = strrchr (tmp,'\\')) ? s : tmp,'.')) *s = '\0';
  292.   stream->mailbox = cpystr (tmp);
  293.   LOCAL->fd = fd;        /* note the file */
  294.   LOCAL->filesize = 0;        /* initialize parsed file size */
  295.   stream->sequence++;        /* bump sequence number */
  296.                 /* parse mailbox */
  297.   stream->nmsgs = stream->recent = 0;
  298.   if (!dawz_ping (stream)) return NIL;
  299.   if (!stream->nmsgs) mm_log ("Mailbox is empty",(long) NIL);
  300.   return stream;        /* return stream to caller */
  301. }
  302.  
  303. /* Dawz mail close
  304.  * Accepts: MAIL stream
  305.  */
  306.  
  307. void dawz_close (MAILSTREAM *stream)
  308. {
  309.   long i;
  310.   if (stream && LOCAL) {    /* only if a file is open */
  311.     close (LOCAL->fd);        /* close the local file */
  312.                 /* nuke the local data */
  313.     fs_give ((void **) &stream->local);
  314.     stream->dtb = NIL;        /* log out the DTB */
  315.   }
  316. }
  317.  
  318.  
  319. /* Dawz mail fetch fast information
  320.  * Accepts: MAIL stream
  321.  *        sequence
  322.  */
  323.  
  324. void dawz_fetchfast (MAILSTREAM *stream,char *sequence)
  325. {
  326.   return;            /* no-op for local mail */
  327. }
  328.  
  329.  
  330. /* Dawz mail fetch flags
  331.  * Accepts: MAIL stream
  332.  *        sequence
  333.  */
  334.  
  335. void dawz_fetchflags (MAILSTREAM *stream,char *sequence)
  336. {
  337.   return;            /* no-op for local mail */
  338. }
  339.  
  340. /* Dawz string driver for file stringstructs */
  341.  
  342. STRINGDRIVER dawz_string = {
  343.   dawz_string_init,        /* initialize string structure */
  344.   dawz_string_next,        /* get next byte in string structure */
  345.   dawz_string_setpos        /* set position in string structure */
  346. };
  347.  
  348.  
  349. /* Cache buffer for file stringstructs */
  350.  
  351. #define DOSCHUNKLEN 4096
  352. char dos_chunk[DOSCHUNKLEN];
  353.  
  354.  
  355. /* Initialize dawz string structure for file stringstruct
  356.  * Accepts: string structure
  357.  *        pointer to string
  358.  *        size of string
  359.  */
  360.  
  361. void dawz_string_init (STRING *s,void *data,unsigned long size)
  362. {
  363.   DAWZDATA *d = (DAWZDATA *) data;
  364.   s->data = (void *) d->fd;    /* note fd */
  365.   s->data1 = d->pos;        /* note file offset */
  366.   s->size = size;        /* note size */
  367.   s->curpos = s->chunk = dos_chunk;
  368.   s->chunksize = (unsigned long) DOSCHUNKLEN;
  369.   s->offset = 0;        /* initial position */
  370.                 /* and size of data */
  371.   s->cursize = min ((long) DOSCHUNKLEN,size);
  372.                 /* move to that position in the file */
  373.   lseek (d->fd,d->pos,SEEK_SET);
  374.   read (d->fd,s->chunk,(unsigned int) s->cursize);
  375. }
  376.  
  377. /* Get next character from file stringstruct
  378.  * Accepts: string structure
  379.  * Returns: character, string structure chunk refreshed
  380.  */
  381.  
  382. char dawz_string_next (STRING *s)
  383. {
  384.   char c = *s->curpos++;    /* get next byte */
  385.                 /* move to next chunk */
  386.   SETPOS (s,s->offset + s->chunksize);
  387.   return c;            /* return the byte */
  388. }
  389.  
  390.  
  391. /* Set string pointer position for file stringstruct
  392.  * Accepts: string structure
  393.  *        new position
  394.  */
  395.  
  396. void dawz_string_setpos (STRING *s,unsigned long i)
  397. {
  398.   s->offset = i;        /* set new offset */
  399.   s->curpos = s->chunk;        /* reset position */
  400.                 /* set size of data */
  401.   if (s->cursize = s->size > s->offset ? min ((long) DOSCHUNKLEN,SIZE (s)):0) {
  402.                 /* move to that position in the file */
  403.     lseek ((int) s->data,s->data1 + s->offset,SEEK_SET);
  404.     read ((int) s->data,s->curpos,(unsigned int) s->cursize);
  405.   }
  406. }
  407.  
  408. /* Dawz mail fetch structure
  409.  * Accepts: MAIL stream
  410.  *        message # to fetch
  411.  *        pointer to return body
  412.  * Returns: envelope of this message, body returned in body value
  413.  *
  414.  * Fetches the "fast" information as well
  415.  */
  416.  
  417. #define MAXHDR (unsigned long) 4*MAILTMPLEN
  418.  
  419. ENVELOPE *dawz_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  420. {
  421.   LONGCACHE *lelt;
  422.   ENVELOPE **env;
  423.   BODY **b;
  424.   STRING bs;
  425.   DAWZDATA d;
  426.   unsigned long hdrsize;
  427.   unsigned long hdrpos = dawz_header (stream,msgno,&hdrsize);
  428.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size - hdrsize;
  429.                 /* limit header size */
  430.   if (hdrsize > MAXHDR) hdrsize = MAXHDR;
  431.   if (stream->scache) {        /* short cache */
  432.     if (msgno != stream->msgno){/* flush old poop if a different message */
  433.       mail_free_envelope (&stream->env);
  434.       mail_free_body (&stream->body);
  435.     }
  436.     stream->msgno = msgno;
  437.     env = &stream->env;        /* get pointers to envelope and body */
  438.     b = &stream->body;
  439.   }
  440.   else {            /* long cache */
  441.     lelt = mail_lelt (stream,msgno);
  442.     env = &lelt->env;        /* get pointers to envelope and body */
  443.     b = &lelt->body;
  444.   }
  445.  
  446.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  447.     char *hdr = (char *) fs_get (hdrsize + 1);
  448.     char *tmp = (char *) fs_get (MAXHDR);
  449.     mail_free_envelope (env);    /* flush old envelope and body */
  450.     mail_free_body (b);
  451.                 /* get to header position */
  452.     lseek (LOCAL->fd,hdrpos,SEEK_SET);
  453.                 /* read the text */
  454.     if (read (LOCAL->fd,hdr,(unsigned int) hdrsize) >= 0) {
  455.       if (hdr[hdrsize-1] != '\012') hdr[hdrsize-1] = '\012';
  456.       hdr[hdrsize] = '\0';    /* make sure tied off */
  457.       d.fd = LOCAL->fd;        /* set initial stringstruct */
  458.       d.pos = hdrpos + hdrsize;
  459.       INIT (&bs,dawz_string,(void *) &d,textsize);
  460.                 /* parse envelope and body */
  461.       rfc822_parse_msg (env,body ? b : NIL,hdr,hdrsize,&bs,mylocalhost (),tmp);
  462.     }
  463.     fs_give ((void **) &tmp);
  464.     fs_give ((void **) &hdr);
  465.   }
  466.   if (body) *body = *b;        /* return the body */
  467.   return *env;            /* return the envelope */
  468. }
  469.  
  470. /* Dawz mail fetch message header
  471.  * Accepts: MAIL stream
  472.  *        message # to fetch
  473.  * Returns: message header in RFC822 format
  474.  */
  475.  
  476. char *dawz_fetchheader (MAILSTREAM *stream,long msgno)
  477. {
  478.   unsigned long hdrsize;
  479.   unsigned long hdrpos = dawz_header (stream,msgno,&hdrsize);
  480.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  481.   if (stream->text) fs_give ((void **) &stream->text);
  482.                 /* get to header position */
  483.   lseek (LOCAL->fd,hdrpos,SEEK_SET);
  484.   return stream->text = (mg ? *mg : mm_gets) (dawz_read,stream,hdrsize);
  485. }
  486.  
  487.  
  488. /* Dawz mail fetch message text (body only)
  489.  * Accepts: MAIL stream
  490.  *        message # to fetch
  491.  * Returns: message text in RFC822 format
  492.  */
  493.  
  494. char *dawz_fetchtext (MAILSTREAM *stream,long msgno)
  495. {
  496.   unsigned long hdrsize;
  497.   unsigned long hdrpos = dawz_header (stream,msgno,&hdrsize);
  498.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size - hdrsize;
  499.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  500.   if (stream->text) fs_give ((void **) &stream->text);
  501.                 /* mark message as seen */
  502.   mail_elt (stream,msgno)->seen = T;
  503.                 /* recalculate status */
  504.   dawz_update_status (stream,msgno);
  505.                 /* get to text position */
  506.   lseek (LOCAL->fd,hdrpos + hdrsize,SEEK_SET);
  507.   return stream->text = (mg ? *mg : mm_gets) (dawz_read,stream,textsize);
  508. }
  509.  
  510. /* Dawz fetch message body as a structure
  511.  * Accepts: Mail stream
  512.  *        message # to fetch
  513.  *        section specifier
  514.  *        pointer to length
  515.  * Returns: pointer to section of message body
  516.  */
  517.  
  518. char *dawz_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  519. {
  520.   BODY *b;
  521.   PART *pt;
  522.   unsigned long i;
  523.   unsigned long base;
  524.   unsigned long offset = 0;
  525.   unsigned long hdrpos = dawz_header (stream,m,&base);
  526.   MESSAGECACHE *elt = mail_elt (stream,m);
  527.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  528.   if (stream->text) fs_give ((void **) &stream->text);
  529.                 /* make sure have a body */
  530.   if (!(dawz_fetchstructure (stream,m,&b) && b && s && *s &&
  531.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  532.   do {                /* until find desired body part */
  533.                 /* multipart content? */
  534.     if (b->type == TYPEMULTIPART) {
  535.       pt = b->contents.part;    /* yes, find desired part */
  536.       while (--i && (pt = pt->next));
  537.       if (!pt) return NIL;    /* bad specifier */
  538.                 /* note new body, check valid nesting */
  539.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  540.       offset = pt->offset;    /* get new offset */
  541.     }
  542.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  543.                 /* need to go down further? */
  544.     if (i = *s) switch (b->type) {
  545.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  546.       offset = b->contents.msg.offset;
  547.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  548.     case TYPEMULTIPART:        /* multipart, get next section */
  549.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  550.     default:            /* bogus subpart specification */
  551.       return NIL;
  552.     }
  553.   } while (i);
  554.                 /* lose if body bogus */
  555.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  556.   elt->seen = T;        /* mark message as seen */
  557.   dawz_update_status (stream,m);/* recalculate status */
  558.   lseek (LOCAL->fd,hdrpos + base + offset,SEEK_SET);
  559.   return stream->text =
  560.     (mg ? *mg : mm_gets) (dawz_read,stream,*len = b->size.bytes);
  561. }
  562.  
  563. /* Dawz mail read
  564.  * Accepts: MAIL stream
  565.  *        number of bytes to read
  566.  *        buffer address
  567.  * Returns: T if success, NIL otherwise
  568.  */
  569.  
  570. long dawz_read (MAILSTREAM *stream,unsigned long count,char *buffer)
  571. {
  572.   return read (LOCAL->fd,buffer,(unsigned int) count) ? T : NIL;
  573. }
  574.  
  575. /* Dawz locate header for a message
  576.  * Accepts: MAIL stream
  577.  *        message number
  578.  *        pointer to returned header size
  579.  * Returns: position of header in file
  580.  */
  581.  
  582. unsigned long dawz_header (MAILSTREAM *stream,long msgno,unsigned long *size)
  583. {
  584.   long siz;
  585.   long i = 0;
  586.   int q = 0;
  587.   char *s;
  588.   char tmp[MAILTMPLEN];
  589.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  590.   long pos = elt->data1 + (elt->data2 >> 24);
  591.                 /* is size known? */
  592.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  593.                 /* get to header position */
  594.     lseek (LOCAL->fd,pos,SEEK_SET);
  595.                 /* search message for CRLF CRLF */
  596.     for (siz = 0; siz < elt->rfc822_size; siz++) {
  597.                 /* read another buffer as necessary */
  598.       if (--i <= 0)        /* buffer empty? */
  599.     if (read (LOCAL->fd,s = tmp,
  600.            i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0)
  601.       return pos;        /* I/O error? */
  602.       switch (q) {        /* sniff at buffer */
  603.       case 0:            /* first character */
  604.     q = (*s++ == '\015') ? 1 : 0;
  605.     break;
  606.       case 1:            /* second character */
  607.     q = (*s++ == '\012') ? 2 : 0;
  608.     break;
  609.       case 2:            /* third character */
  610.     q = (*s++ == '\015') ? 3 : 0;
  611.     break;
  612.       case 3:            /* fourth character */
  613.     if (*s++ == '\012') {    /* have the sequence? */
  614.                 /* yes, note for later */
  615.       elt->data2 |= (*size = siz);
  616.       return pos;        /* return to caller */
  617.     }
  618.     q = 0;            /* lost... */
  619.     break;
  620.       }
  621.     }
  622.   }
  623.   return pos;            /* have position */
  624. }
  625.  
  626. /* Dawz mail set flag
  627.  * Accepts: MAIL stream
  628.  *        sequence
  629.  *        flag(s)
  630.  */
  631.  
  632. void dawz_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  633. {
  634.   MESSAGECACHE *elt;
  635.   long i;
  636.   short f = dawz_getflags (stream,flag);
  637.   if (!f) return;        /* no-op if no flags to modify */
  638.                 /* get sequence and loop on it */
  639.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  640.     if ((elt = mail_elt (stream,i))->sequence) {
  641.                 /* set all requested flags */
  642.       if (f&fSEEN) elt->seen = T;
  643.       if (f&fDELETED) elt->deleted = T;
  644.       if (f&fFLAGGED) elt->flagged = T;
  645.       if (f&fANSWERED) elt->answered = T;
  646.                 /* recalculate status */
  647.       dawz_update_status (stream,i);
  648.     }
  649. }
  650.  
  651. /* Dawz mail clear flag
  652.  * Accepts: MAIL stream
  653.  *        sequence
  654.  *        flag(s)
  655.  */
  656.  
  657. void dawz_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  658. {
  659.   MESSAGECACHE *elt;
  660.   long i;
  661.   short f = dawz_getflags (stream,flag);
  662.   if (!f) return;        /* no-op if no flags to modify */
  663.                 /* get sequence and loop on it */
  664.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  665.     if ((elt = mail_elt (stream,i))->sequence) {
  666.                 /* clear all requested flags */
  667.       if (f&fSEEN) elt->seen = NIL;
  668.       if (f&fDELETED) elt->deleted = NIL;
  669.       if (f&fFLAGGED) elt->flagged = NIL;
  670.       if (f&fANSWERED) elt->answered = NIL;
  671.                 /* recalculate status */
  672.       dawz_update_status (stream,i);
  673.     }
  674. }
  675.  
  676. /* Dawz mail search for messages
  677.  * Accepts: MAIL stream
  678.  *        search criteria
  679.  */
  680.  
  681. void dawz_search (MAILSTREAM *stream,char *criteria)
  682. {
  683.   long i,n;
  684.   char *d;
  685.   search_t f;
  686.                 /* initially all searched */
  687.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  688.                 /* get first criterion */
  689.   if (criteria && (criteria = strtok (criteria," "))) {
  690.                 /* for each criterion */
  691.     for (; criteria; (criteria = strtok (NIL," "))) {
  692.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  693.       switch (*ucase (criteria)) {
  694.       case 'A':            /* possible ALL, ANSWERED */
  695.     if (!strcmp (criteria+1,"LL")) f = dawz_search_all;
  696.     else if (!strcmp (criteria+1,"NSWERED")) f = dawz_search_answered;
  697.     break;
  698.       case 'B':            /* possible BCC, BEFORE, BODY */
  699.     if (!strcmp (criteria+1,"CC"))
  700.       f = dawz_search_string (dawz_search_bcc,&d,&n);
  701.     else if (!strcmp (criteria+1,"EFORE"))
  702.       f = dawz_search_date (dawz_search_before,&n);
  703.     else if (!strcmp (criteria+1,"ODY"))
  704.       f = dawz_search_string (dawz_search_body,&d,&n);
  705.     break;
  706.       case 'C':            /* possible CC */
  707.     if (!strcmp (criteria+1,"C")) 
  708.       f = dawz_search_string (dawz_search_cc,&d,&n);
  709.     break;
  710.       case 'D':            /* possible DELETED */
  711.     if (!strcmp (criteria+1,"ELETED")) f = dawz_search_deleted;
  712.     break;
  713.       case 'F':            /* possible FLAGGED, FROM */
  714.     if (!strcmp (criteria+1,"LAGGED")) f = dawz_search_flagged;
  715.     else if (!strcmp (criteria+1,"ROM"))
  716.       f = dawz_search_string (dawz_search_from,&d,&n);
  717.     break;
  718.       case 'K':            /* possible KEYWORD */
  719.     if (!strcmp (criteria+1,"EYWORD"))
  720.       f = dawz_search_flag (dawz_search_keyword,&n,stream);
  721.     break;
  722.       case 'N':            /* possible NEW */
  723.     if (!strcmp (criteria+1,"EW")) f = dawz_search_new;
  724.     break;
  725.  
  726.       case 'O':            /* possible OLD, ON */
  727.     if (!strcmp (criteria+1,"LD")) f = dawz_search_old;
  728.     else if (!strcmp (criteria+1,"N"))
  729.       f = dawz_search_date (dawz_search_on,&n);
  730.     break;
  731.       case 'R':            /* possible RECENT */
  732.     if (!strcmp (criteria+1,"ECENT")) f = dawz_search_recent;
  733.     break;
  734.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  735.     if (!strcmp (criteria+1,"EEN")) f = dawz_search_seen;
  736.     else if (!strcmp (criteria+1,"INCE"))
  737.       f = dawz_search_date (dawz_search_since,&n);
  738.     else if (!strcmp (criteria+1,"UBJECT"))
  739.       f = dawz_search_string (dawz_search_subject,&d,&n);
  740.     break;
  741.       case 'T':            /* possible TEXT, TO */
  742.     if (!strcmp (criteria+1,"EXT"))
  743.       f = dawz_search_string (dawz_search_text,&d,&n);
  744.     else if (!strcmp (criteria+1,"O"))
  745.       f = dawz_search_string (dawz_search_to,&d,&n);
  746.     break;
  747.       case 'U':            /* possible UN* */
  748.     if (criteria[1] == 'N') {
  749.       if (!strcmp (criteria+2,"ANSWERED")) f = dawz_search_unanswered;
  750.       else if (!strcmp (criteria+2,"DELETED")) f = dawz_search_undeleted;
  751.       else if (!strcmp (criteria+2,"FLAGGED")) f = dawz_search_unflagged;
  752.       else if (!strcmp (criteria+2,"KEYWORD"))
  753.         f = dawz_search_flag (dawz_search_unkeyword,&n,stream);
  754.       else if (!strcmp (criteria+2,"SEEN")) f = dawz_search_unseen;
  755.     }
  756.     break;
  757.       default:            /* we will barf below */
  758.     break;
  759.       }
  760.       if (!f) {            /* if can't determine any criteria */
  761.     mm_log ("Unknown search criterion",ERROR);
  762.     return;
  763.       }
  764.                 /* run the search criterion */
  765.       for (i = 1; i <= stream->nmsgs; ++i)
  766.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  767.       mail_elt (stream,i)->searched = NIL;
  768.     }
  769.                 /* report search results to main program */
  770.     for (i = 1; i <= stream->nmsgs; ++i)
  771.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  772.   }
  773. }
  774.  
  775. /* Dawz mail ping mailbox
  776.  * Accepts: MAIL stream
  777.  * Returns: T if stream still alive, NIL if not
  778.  */
  779.  
  780. long dawz_ping (MAILSTREAM *stream)
  781. {
  782.   long i = 0;
  783.   long r,j;
  784.   struct stat sbuf;
  785.                 /* punt if stream no longer alive */
  786.   if (!(stream && LOCAL)) return NIL;
  787.                 /* parse mailbox, punt if parse dies */
  788.   return (dawz_parse (stream)) ? T : NIL;
  789. }
  790.  
  791.  
  792. /* Dawz mail check mailbox (reparses status too)
  793.  * Accepts: MAIL stream
  794.  */
  795.  
  796. void dawz_check (MAILSTREAM *stream)
  797. {
  798.   long i = 1;
  799.   if (dawz_ping (stream)) {    /* ping mailbox */
  800.                 /* get new message status */
  801.     while (i <= stream->nmsgs) mail_elt (stream,i++);
  802.     mm_log ("Check completed",(long) NIL);
  803.   }
  804. }
  805.  
  806. /* Dawz mail expunge mailbox
  807.  * Accepts: MAIL stream
  808.  */
  809.  
  810. void dawz_expunge (MAILSTREAM *stream)
  811. {
  812.   unsigned long i = 1;
  813.   unsigned long j,k,m,recent;
  814.   unsigned long n = 0;
  815.   unsigned long delta = 0;
  816.   MESSAGECACHE *elt;
  817.   char tmp[MAILTMPLEN];
  818.                 /* do nothing if stream dead */
  819.   if (!dawz_ping (stream)) return;
  820.   if (stream->rdonly) {        /* won't do on readonly files! */
  821.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  822.     return;
  823.   }
  824.   mm_critical (stream);        /* go critical */
  825.   recent = stream->recent;    /* get recent now that pinged */ 
  826.   while (i <= stream->nmsgs) {    /* for each message */
  827.                 /* if deleted */
  828.     if ((elt = mail_elt (stream,i))->deleted) {
  829.       if (elt->recent) --recent;/* if recent, note one less recent message */
  830.                 /* number of bytes to delete */
  831.       delta += (elt->data2 >> 24) + elt->rfc822_size;
  832.       mail_expunged (stream,i);    /* notify upper levels */
  833.       n++;            /* count up one more deleted message */
  834.     }
  835.     else if (i++ && delta) {    /* preserved message */
  836.       j = elt->data1;        /* j is byte to copy, k is number of bytes */
  837.       k = (elt->data2 >> 24) + elt->rfc822_size;
  838.       do {            /* read from source position */
  839.     m = min (k,(unsigned long) MAILTMPLEN);
  840.     lseek (LOCAL->fd,j,SEEK_SET);
  841.     read (LOCAL->fd,tmp,(unsigned int) m);
  842.                 /* write to destination position */
  843.     lseek (LOCAL->fd,j - delta,SEEK_SET);
  844.     write (LOCAL->fd,tmp,(unsigned int) m);
  845.     j += m;            /* next chunk, perhaps */
  846.       } while (k -= m);        /* until done */
  847.       elt->data1 -= delta;    /* note the new address of this text */
  848.     }
  849.   }
  850.   if (n) {            /* truncate file after last message */
  851.     chsize (LOCAL->fd,LOCAL->filesize -= delta);
  852.     sprintf (tmp,"Expunged %ld messages",n);
  853.     mm_log (tmp,(long) NIL);    /* output the news */
  854.   }
  855.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  856.   mm_nocritical (stream);    /* release critical */
  857.                 /* notify upper level of new mailbox size */
  858.   mail_exists (stream,stream->nmsgs);
  859.   mail_recent (stream,recent);
  860. }
  861.  
  862. /* Dawz mail copy message(s)
  863.  * Accepts: MAIL stream
  864.  *        sequence
  865.  *        destination mailbox
  866.  * Returns: T if success, NIL if failed
  867.  */
  868.  
  869. long dawz_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  870. {
  871.                 /* copy the messages */
  872.   return (mail_sequence (stream,sequence)) ?
  873.     dawz_copy_messages (stream,mailbox) : NIL;
  874. }
  875.  
  876.  
  877. /* Dawz mail move message(s)
  878.  * Accepts: MAIL stream
  879.  *        sequence
  880.  *        destination mailbox
  881.  * Returns: T if success, NIL if failed
  882.  */
  883.  
  884. long dawz_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  885. {
  886.   long i;
  887.   MESSAGECACHE *elt;
  888.   if (!(mail_sequence (stream,sequence) &&
  889.     dawz_copy_messages (stream,mailbox))) return NIL;
  890.                 /* delete all requested messages */
  891.   for (i = 1; i <= stream->nmsgs; i++)
  892.     if ((elt = mail_elt (stream,i))->sequence) {
  893.       elt->deleted = T;        /* mark message deleted */
  894.                 /* recalculate status */
  895.       dawz_update_status (stream,i);
  896.     }
  897.   return T;
  898. }
  899.  
  900. /* Dawz mail append message from stringstruct
  901.  * Accepts: MAIL stream
  902.  *        destination mailbox
  903.  *        stringstruct of messages to append
  904.  * Returns: T if append successful, else NIL
  905.  */
  906.  
  907. long dawz_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  908.           STRING *message)
  909. {
  910.   struct stat sbuf;
  911.   int fd,zone;
  912.   char tmp[MAILTMPLEN];
  913.   MESSAGECACHE elt;
  914.   long i;
  915.   long size = SIZE (message);
  916.   short f = dawz_getflags (stream,flags);
  917.   if (date) {            /* want to preserve date? */
  918.                 /* yes, parse date into an elt */
  919.     if (!mail_parse_date (&elt,date)) {
  920.       sprintf (tmp,"Bad date in append: %s",date);
  921.       mm_log (tmp,ERROR);
  922.       return NIL;
  923.     }
  924.   }
  925.   if (!dawz_isvalid (mailbox)) switch (errno) {
  926.   case ENOENT:            /* no such file? */
  927.     mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
  928.            (long) NIL);
  929.     return NIL;
  930.   case 0:            /* merely empty file? */
  931.     break;
  932.   case EINVAL:            /* name is bogus */
  933.     return dawz_badname (tmp,mailbox);
  934.   default:            /* file exists, but not valid format */
  935.     sprintf (tmp,"Not a Dawz-format mailbox: %s",mailbox);
  936.     mm_log (tmp,ERROR);
  937.     return NIL;
  938.   }
  939.                 /* open the destination */
  940.   if ((fd = open (mailboxfile (tmp,mailbox),
  941.           O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  942.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  943.     mm_log (tmp,ERROR);
  944.     return NIL;
  945.   }
  946.   mm_critical (stream);        /* go critical */
  947.   fstat (fd,&sbuf);        /* get current file size */
  948.   zone = -(((int)timezone)/ 60);/* get timezone from TZ environment stuff */
  949.   if (date) mail_date(tmp,&elt);/* use date if given */
  950.   else internal_date (tmp);    /* else use time now */
  951.                 /* add remainder of header */
  952.   sprintf (tmp + strlen (tmp),",%ld;0000000000%02o\015\012",size,f);
  953.  
  954.                 /* write header */
  955.   if (write (fd,tmp,strlen (tmp)) < 0) {
  956.     sprintf (tmp,"Header write failed: %s",strerror (errno));
  957.     mm_log (tmp,ERROR);
  958.     chsize (fd,sbuf.st_size);
  959.   }  
  960.   else while (size) {        /* while there is more data to write */
  961.     if (write (fd,message->curpos,i = min (size,message->cursize)) < 0) {
  962.       sprintf (tmp,"Message append failed: %s",strerror (errno));
  963.       mm_log (tmp,ERROR);
  964.       chsize (fd,sbuf.st_size);
  965.       break;
  966.     }
  967.     size -= i;            /* note that we wrote out this much */
  968.     message->curpos += i;
  969.     message->cursize -= i;
  970.     (message->dtb->next) (message);
  971.   }
  972.   close (fd);            /* close the file */
  973.   mm_nocritical (stream);    /* release critical */
  974.   return T;            /* return success */
  975. }
  976.  
  977.  
  978. /* Dawz garbage collect stream
  979.  * Accepts: Mail stream
  980.  *        garbage collection flags
  981.  */
  982.  
  983. void dawz_gc (MAILSTREAM *stream,long gcflags)
  984. {
  985.   /* nothing here for now */
  986. }
  987.  
  988. /* Internal routines */
  989.  
  990.  
  991. /* Return bad file name error message
  992.  * Accepts: temporary buffer
  993.  *        file name
  994.  * Returns: long NIL always
  995.  */
  996.  
  997. long dawz_badname (char *tmp,char *s)
  998. {
  999.   sprintf (tmp,"Invalid mailbox name: %s",s);
  1000.   mm_log (tmp,ERROR);
  1001.   return (long) NIL;
  1002. }
  1003.  
  1004. /* Parse flag list
  1005.  * Accepts: MAIL stream
  1006.  *        flag list as a character string
  1007.  * Returns: system flags
  1008.  */
  1009.  
  1010. long dawz_getflags (MAILSTREAM *stream,char *flag)
  1011. {
  1012.   char tmp[MAILTMPLEN];
  1013.   char key[MAILTMPLEN];
  1014.   char *t,*s;
  1015.   short f = 0;
  1016.   long i;
  1017.   short j;
  1018.   if (flag && *flag) {        /* no-op if no flag string */
  1019.                 /* check if a list and make sure valid */
  1020.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1021.       mm_log ("Bad flag list",ERROR);
  1022.       return NIL;
  1023.     }
  1024.                 /* copy the flag string w/o list construct */
  1025.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1026.     tmp[j] = '\0';        /* tie off tail */
  1027.  
  1028.                 /* make uppercase, find first, parse */
  1029.     if (t = strtok (ucase (tmp)," ")) do {
  1030.       i = 0;            /* no flag yet */
  1031.                 /* system flag, dispatch on first character */
  1032.       if (*t == '\\') switch (*++t) {
  1033.       case 'S':            /* possible \Seen flag */
  1034.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1035.       f |= i = fSEEN;
  1036.     break;
  1037.       case 'D':            /* possible \Deleted flag */
  1038.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1039.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1040.     break;
  1041.       case 'F':            /* possible \Flagged flag */
  1042.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1043.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1044.     break;
  1045.       case 'A':            /* possible \Answered flag */
  1046.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1047.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1048.       f |= i = fANSWERED;
  1049.     break;
  1050.       default:            /* unknown */
  1051.     break;
  1052.       }
  1053.       if (!i) {            /* didn't find a matching flag? */
  1054.     sprintf (key,"Unknown flag: %.80s",t);
  1055.     mm_log (key,ERROR);
  1056.       }
  1057.                 /* parse next flag */
  1058.     } while (t = strtok (NIL," "));
  1059.   }
  1060.   return f;
  1061. }
  1062.  
  1063. /* Dawz mail parse mailbox
  1064.  * Accepts: MAIL stream
  1065.  * Returns: T if parse OK
  1066.  *        NIL if failure, stream aborted
  1067.  */
  1068.  
  1069. long dawz_parse (MAILSTREAM *stream)
  1070. {
  1071.   struct stat sbuf;
  1072.   MESSAGECACHE *elt = NIL;
  1073.   char c,*s,*t,*x;
  1074.   char lbuf[65],tmp[MAILTMPLEN];
  1075.   long i;
  1076.   long curpos = LOCAL->filesize;
  1077.   long nmsgs = stream->nmsgs;
  1078.   long recent = stream->recent;
  1079.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1080.   if (sbuf.st_size < curpos) {    /* sanity check */
  1081.     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
  1082.     mm_log (tmp,ERROR);
  1083.     dawz_close (stream);
  1084.     return NIL;
  1085.   }
  1086.                 /* while there is stuff to parse */
  1087.   while (i = sbuf.st_size - curpos) {
  1088.                 /* get to that position in the file */
  1089.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1090.     if ((i = read (LOCAL->fd,lbuf,64)) <= 0) {
  1091.       sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
  1092.            curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
  1093.       mm_log (tmp,ERROR);
  1094.       dawz_close (stream);
  1095.       return NIL;
  1096.     }
  1097.     lbuf[i] = '\0';        /* tie off buffer just in case */
  1098.     if (!((s = strchr (lbuf,'\015')) && (s[1] == '\012'))) {
  1099.       sprintf (tmp,"Unable to find end of line at %ld in %ld bytes, text: %s",
  1100.            curpos,i,lbuf);
  1101.       mm_log (tmp,ERROR);
  1102.       dawz_close (stream);
  1103.       return NIL;
  1104.     }
  1105.     *s = '\0';            /* tie off header line */
  1106.     i = (s + 2) - lbuf;        /* note start of text offset */
  1107.     if (!((s = strchr (lbuf,',')) && (t = strchr (s+1,';')))) {
  1108.       sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,lbuf);
  1109.       mm_log (tmp,ERROR);
  1110.       dawz_close (stream);
  1111.       return NIL;
  1112.     }
  1113.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1114.                 /* intantiate an elt for this message */
  1115.     (elt = mail_elt (stream,++nmsgs))->valid = T;
  1116.     elt->data1 = curpos;    /* note file offset of header */
  1117.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1118.                 /* parse the header components */
  1119.     if (!(mail_parse_date (elt,lbuf) &&
  1120.       (elt->rfc822_size = strtol (x = s,&s,10)) && (!(s && *s)) &&
  1121.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1122.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1123.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1124.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1125.       sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
  1126.            curpos,lbuf,x,t);
  1127.       dawz_close (stream);
  1128.       return NIL;
  1129.     }
  1130.                 /* update current position to next header */
  1131.     curpos += i + elt->rfc822_size;
  1132.                 /* calculate system flags */
  1133.     if ((i = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1134.     if (i & fDELETED) elt->deleted = T;
  1135.     if (i & fFLAGGED) elt->flagged = T;
  1136.     if (i & fANSWERED) elt->answered = T;
  1137.     if (curpos > sbuf.st_size) {
  1138.       mm_log ("Last message runs past end of file",ERROR);
  1139.       dawz_close (stream);
  1140.       return NIL;
  1141.     }
  1142.   }
  1143.                 /* update parsed file size */
  1144.   LOCAL->filesize = sbuf.st_size;
  1145.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1146.   mail_recent (stream,recent);    /* and of change in recent messages */
  1147.   return T;            /* return the winnage */
  1148. }
  1149.  
  1150. /* Dawz copy messages
  1151.  * Accepts: MAIL stream
  1152.  *        mailbox copy vector
  1153.  *        mailbox name
  1154.  * Returns: T if success, NIL if failed
  1155.  */
  1156.  
  1157. long dawz_copy_messages (MAILSTREAM *stream,char *mailbox)
  1158. {
  1159.   char tmp[MAILTMPLEN];
  1160.   struct stat sbuf;
  1161.   MESSAGECACHE *elt;
  1162.   unsigned long i,j,k;
  1163.   long ret = LONGT;
  1164.   int fd;
  1165.   if (!dawz_isvalid (mailbox)) switch (errno) {
  1166.   case ENOENT:            /* no such file? */
  1167.     mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
  1168.            (long) NIL);
  1169.     return NIL;
  1170.   case 0:            /* merely empty file? */
  1171.     break;
  1172.   case EINVAL:            /* name is bogus */
  1173.     return dawz_badname (tmp,mailbox);
  1174.   default:            /* file exists, but not valid format */
  1175.     sprintf (tmp,"Not a Dawz-format mailbox: %s",mailbox);
  1176.     mm_log (tmp,ERROR);
  1177.     return NIL;
  1178.   }
  1179.                 /* open the destination */
  1180.   if ((fd = open (mailboxfile (tmp,mailbox),
  1181.           O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
  1182.     sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
  1183.     mm_log (tmp,ERROR);
  1184.     return NIL;
  1185.   }
  1186.   mm_critical (stream);        /* go critical */
  1187.   fstat (fd,&sbuf);        /* get current file size */
  1188.                 /* for each requested message */
  1189.   for (i = 1; ret && (i <= stream->nmsgs); i++)
  1190.     if ((elt = mail_elt (stream,i))->sequence) {
  1191.       lseek (LOCAL->fd,elt->data1,SEEK_SET);
  1192.                 /* number of bytes to copy */
  1193.       k = (elt->data2 >> 24) + elt->rfc822_size;
  1194.       do {            /* read from source position */
  1195.     j = min (k,(long) MAILTMPLEN);
  1196.     read (LOCAL->fd,tmp,(unsigned int) j);
  1197.     if (write (fd,tmp,(unsigned int) j) < 0) {
  1198.       sprintf (tmp,"Unable to write message: %s",strerror (errno));
  1199.       mm_log (tmp,ERROR);
  1200.       chsize (fd,sbuf.st_size);
  1201.       j = k;
  1202.       ret = NIL;        /* note error */
  1203.       break;
  1204.     }
  1205.       } while (k -= j);        /* until done */
  1206.     }
  1207.   close (fd);            /* close the file */
  1208.   mm_nocritical (stream);    /* release critical */
  1209.   return ret;
  1210. }
  1211.  
  1212. /* Dawz update status string
  1213.  * Accepts: MAIL stream
  1214.  *        message number
  1215.  */
  1216.  
  1217. void dawz_update_status (MAILSTREAM *stream,long msgno)
  1218. {
  1219.   char tmp[MAILTMPLEN];
  1220.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1221.   unsigned long j,k = 0;
  1222.   if (stream->rdonly) return;    /* not if readonly you don't */
  1223.   j = elt->user_flags;        /* get user flags */
  1224.                 /* reverse bits (dontcha wish we had CIRC?) */
  1225.   while (j) k |= 1 << 29 - find_rightmost_bit (&j);
  1226.                 /* print new flag string */
  1227.   sprintf (tmp,"%010lo%02o",k,(fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1228.        (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1229.                 /* get to that place in the file */
  1230.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 14,SEEK_SET);
  1231.   write (LOCAL->fd,tmp,12);    /* write new flags */
  1232. }
  1233.  
  1234. /* Search support routines
  1235.  * Accepts: MAIL stream
  1236.  *        message number
  1237.  *        pointer to additional data
  1238.  * Returns: T if search matches, else NIL
  1239.  */
  1240.  
  1241.  
  1242. char dawz_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1243. {
  1244.   return T;            /* ALL always succeeds */
  1245. }
  1246.  
  1247.  
  1248. char dawz_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1249. {
  1250.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1251. }
  1252.  
  1253.  
  1254. char dawz_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1255. {
  1256.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1257. }
  1258.  
  1259.  
  1260. char dawz_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1261. {
  1262.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1263. }
  1264.  
  1265.  
  1266. char dawz_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1267. {
  1268.   return NIL;
  1269. }
  1270.  
  1271.  
  1272. char dawz_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1273. {
  1274.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1275.   return (elt->recent && !elt->seen) ? T : NIL;
  1276. }
  1277.  
  1278. char dawz_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1279. {
  1280.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1281. }
  1282.  
  1283.  
  1284. char dawz_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1285. {
  1286.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1287. }
  1288.  
  1289.  
  1290. char dawz_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1291. {
  1292.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1293. }
  1294.  
  1295.  
  1296. char dawz_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1297. {
  1298.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1299. }
  1300.  
  1301.  
  1302. char dawz_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1303. {
  1304.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1305. }
  1306.  
  1307.  
  1308. char dawz_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1309. {
  1310.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1311. }
  1312.  
  1313.  
  1314. char dawz_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1315. {
  1316.   return NIL;
  1317. }
  1318.  
  1319.  
  1320. char dawz_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1321. {
  1322.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1323. }
  1324.  
  1325. char dawz_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1326. {
  1327.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1328.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1329. }
  1330.  
  1331.  
  1332. char dawz_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1333. {
  1334.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1335.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1336. }
  1337.  
  1338.  
  1339. char dawz_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1340. {
  1341.                 /* everybody interprets "since" as .GE. */
  1342.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1343.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1344. }
  1345.  
  1346. #define BUFLEN 4*MAILTMPLEN
  1347.  
  1348. char dawz_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1349. {
  1350.   char tmp[BUFLEN];
  1351.   unsigned long bufsize,hdrsize;
  1352.   unsigned long curpos = dawz_header (stream,msgno,&hdrsize);
  1353.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size - hdrsize;
  1354.                 /* get to header position */
  1355.   lseek (LOCAL->fd,curpos += hdrsize,SEEK_SET);
  1356.   while (textsize) {
  1357.     bufsize = min (textsize,(unsigned long) BUFLEN);
  1358.     read (LOCAL->fd,tmp,(unsigned int) bufsize);
  1359.     if (search (tmp,bufsize,d,n)) return T;
  1360.                 /* backtrack by pattern size if not at end */
  1361.     if (bufsize != textsize) bufsize -= n;
  1362.     textsize -= bufsize;    /* this many bytes handled */
  1363.     curpos += bufsize;        /* advance to that point */
  1364.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1365.   }
  1366.   return NIL;            /* not found */
  1367. }
  1368.  
  1369.  
  1370. char dawz_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1371. {
  1372.   char *s = dawz_fetchstructure (stream,msgno,NIL)->subject;
  1373.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1374. }
  1375.  
  1376. char dawz_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1377. {
  1378.   char tmp[BUFLEN];
  1379.   unsigned long bufsize,hdrsize;
  1380.   unsigned long curpos = dawz_header (stream,msgno,&hdrsize);
  1381.   unsigned long textsize = mail_elt (stream,msgno)->rfc822_size;
  1382.                 /* get to header position */
  1383.   lseek (LOCAL->fd,curpos,SEEK_SET);
  1384.   while (textsize) {
  1385.     bufsize = min (textsize,(unsigned long) BUFLEN);
  1386.     read (LOCAL->fd,tmp,(unsigned int) bufsize);
  1387.     if (search (tmp,bufsize,d,n)) return T;
  1388.                 /* backtrack by pattern size if not at end */
  1389.     if (bufsize != textsize) bufsize -= n;
  1390.     textsize -= bufsize;    /* this many bytes handled */
  1391.     curpos += bufsize;        /* advance to that point */
  1392.     lseek (LOCAL->fd,curpos,SEEK_SET);
  1393.   }
  1394.   return NIL;            /* not found */
  1395. }
  1396.  
  1397. char dawz_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1398. {
  1399.   char tmp[8*MAILTMPLEN];
  1400.   tmp[0] = '\0';        /* initially empty string */
  1401.                 /* get text for address */
  1402.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->bcc);
  1403.   return search (tmp,(long) strlen (tmp),d,n);
  1404. }
  1405.  
  1406.  
  1407. char dawz_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1408. {
  1409.   char tmp[8*MAILTMPLEN];
  1410.   tmp[0] = '\0';        /* initially empty string */
  1411.                 /* get text for address */
  1412.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->cc);
  1413.   return search (tmp,(long) strlen (tmp),d,n);
  1414. }
  1415.  
  1416.  
  1417. char dawz_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1418. {
  1419.   char tmp[8*MAILTMPLEN];
  1420.   tmp[0] = '\0';        /* initially empty string */
  1421.                 /* get text for address */
  1422.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->from);
  1423.   return search (tmp,(long) strlen (tmp),d,n);
  1424. }
  1425.  
  1426.  
  1427. char dawz_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1428. {
  1429.   char tmp[8*MAILTMPLEN];
  1430.   tmp[0] = '\0';        /* initially empty string */
  1431.                 /* get text for address */
  1432.   rfc822_write_address (tmp,dawz_fetchstructure (stream,msgno,NIL)->to);
  1433.   return search (tmp,(long) strlen (tmp),d,n);
  1434. }
  1435.  
  1436. /* Search parsers */
  1437.  
  1438.  
  1439. /* Parse a date
  1440.  * Accepts: function to return
  1441.  *        pointer to date integer to return
  1442.  * Returns: function to return
  1443.  */
  1444.  
  1445. search_t dawz_search_date (search_t f,long *n)
  1446. {
  1447.   long i;
  1448.   char *s;
  1449.   MESSAGECACHE elt;
  1450.                 /* parse the date and return fn if OK */
  1451.   return (dawz_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1452.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1453. }
  1454.  
  1455. /* Parse a flag
  1456.  * Accepts: function to return
  1457.  *        pointer to keyword integer to return
  1458.  *        MAIL stream
  1459.  * Returns: function to return
  1460.  */
  1461.  
  1462. search_t dawz_search_flag (search_t f,long *n,MAILSTREAM *stream)
  1463. {
  1464.   strtok (NIL," ");        /* slurp keyword */
  1465.   return f;
  1466. }
  1467.  
  1468. /* Parse a string
  1469.  * Accepts: function to return
  1470.  *        pointer to string to return
  1471.  *        pointer to string length to return
  1472.  * Returns: function to return
  1473.  */
  1474.  
  1475.  
  1476. search_t dawz_search_string (search_t f,char **d,long *n)
  1477. {
  1478.   char *end = " ";
  1479.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1480.   if (!c) return NIL;        /* missing argument */
  1481.   switch (*c) {            /* see what the argument is */
  1482.   case '{':            /* literal string */
  1483.     *n = strtol (c+1,d,10);    /* get its length */
  1484.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1485.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1486.       char e = *--c;
  1487.       *c = DELIM;        /* make sure not a space */
  1488.       strtok (c," ");        /* reset the strtok mechanism */
  1489.       *c = e;            /* put character back */
  1490.       break;
  1491.     }
  1492.   case '\0':            /* catch bogons */
  1493.   case ' ':
  1494.     return NIL;
  1495.   case '"':            /* quoted string */
  1496.     if (strchr (c+1,'"')) end = "\"";
  1497.     else return NIL;
  1498.   default:            /* atomic string */
  1499.     if (*d = strtok (c,end)) *n = strlen (*d);
  1500.     else return NIL;
  1501.     break;
  1502.   }
  1503.   return f;
  1504. }
  1505.